/*
 * KKGestureListener.cpp
 *
 *  Created on: 2013-9-15
 *      Author: wanghua
 */


#include "KKGestureDirector.h"
#include "KKTime.h"
#include "KKNode.h"

KKGestureDirector::KKGestureDirector() {
	m_GestureListener = NULL;
	m_singleTap = false;
	m_longTap = false;
	m_flingEnabled = false;
	m_touchHolder = NULL;

	m_firstTouch = NULL;
	m_secondTouch = NULL;

	m_target = NULL;
}

KKGestureDirector::~KKGestureDirector() {
	CCDirector::sharedDirector()->getScheduler()->unscheduleAllForTarget(this);
	CC_SAFE_DELETE(m_touchHolder);
	CC_SAFE_DELETE(m_firstTouch);
	CC_SAFE_DELETE(m_secondTouch);
}

KKGestureDirector* KKGestureDirector::create(KKNode* target) {
	KKGestureDirector *pobDirector = new KKGestureDirector();
	if (pobDirector) {
		pobDirector->setGestureListener(target);
		pobDirector->setTarget(target);
		pobDirector->retain();
		pobDirector->autorelease();

		return pobDirector;
	}
	CC_SAFE_DELETE(pobDirector);
	return NULL;
}

bool KKGestureDirector::onTouchDown(KKTouch& touch) {

	if (!m_touchHolder) {
		m_touchHolder = new KKTouchHolder();
	}

	KKTouch* currentTouch = m_touchHolder->updateTouch(touch);

	m_singleTap = true;
	m_longTap = false;

	CCDirector::sharedDirector()->getScheduler()->unscheduleAllForTarget(this);

	CCDirector::sharedDirector()->getScheduler()->scheduleSelector(
			schedule_selector(KKGestureDirector::onCheckSingleTap), this,
			ccTime(0.3f), false);

	CCDirector::sharedDirector()->getScheduler()->scheduleSelector(
			schedule_selector(KKGestureDirector::onCheckLongTap), this,
			ccTime(1.5f), false);

	if (touch.getBubbled() && m_GestureListener) {
		if (m_flingEnabled) {
			velocityTracker.trackStart(touch);
		}

		if (m_target->getIsZoomable() || m_target->getIsRotatable()|| m_target->getIsMovable()) {
			if (!m_firstTouch) {
				m_firstTouch = new KKTouch(touch);
			} else if (!m_secondTouch) {
				m_secondTouch = new KKTouch(touch);
			}
		}

		currentTouch->setHandled(true);
		currentTouch->setBubbled(true);

		m_GestureListener->onDown(*currentTouch);

		touch.setBubbled(currentTouch->getBubbled());
		touch.setHandled(currentTouch->getHandled());

		return true;
	}

	return false;
}

#if (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#define MIN_MOVE_LEN 0
#else
#define MIN_MOVE_LEN 3
#endif

#define MAX_SIZE 2048
#define MIN_SIZE 100
#define MIN_SCALE_DELTA 2
#define MIN_ROTATE_DELTA 0

void KKGestureDirector::onTouchMove(KKTouch& touch) {

	KKTouch* currentTouch = m_touchHolder->getTouch(touch.getViewId());

	if (currentTouch) {

		CCPoint currentPoint = currentTouch->getRawPoint();
		CCPoint point = touch.getRawPoint();

		if (abs(currentPoint.x - point.x) > MIN_MOVE_LEN || abs(
				currentPoint.y - point.y) > MIN_MOVE_LEN) {

			currentTouch = m_touchHolder->updateTouch(touch);

			currentTouch->setHandled(true);
			currentTouch->setBubbled(true);
			currentTouch->setIsMoved(true);

			if (touch.getBubbled() && m_GestureListener) {
				m_GestureListener->onScroll(
						*currentTouch,
						ccpSub(currentTouch->getPoint(),
								currentTouch->getPrevPoint()));

				if (m_flingEnabled) {
					velocityTracker.trackPoint(touch);
				}

				if (m_target->getIsZoomable() || m_target->getIsRotatable()|| m_target->getIsMovable()) {
					onGensture(*currentTouch);
				}

				touch.setBubbled(currentTouch->getBubbled());
				touch.setHandled(currentTouch->getHandled());
			}

			m_singleTap = false;
			m_longTap = false;

			CCDirector::sharedDirector()->getScheduler()->unscheduleAllForTarget(
					this);
		}
	}

}

void KKGestureDirector::onTouchUp(KKTouch& touch) {

	KKTouch* currentTouch = m_touchHolder->getTouch(touch.getViewId());

	if (currentTouch) {

		CCDirector::sharedDirector()->getScheduler()->unscheduleAllForTarget(this);

		if (touch.getBubbled() && m_GestureListener) {

			currentTouch = m_touchHolder->updateTouch(touch);

			currentTouch->setHandled(true);
			currentTouch->setBubbled(true);

			m_GestureListener->onUp(*currentTouch);

			if (m_target->checkInTapArea(currentTouch->getPoint())) {
				m_GestureListener->onTap(*currentTouch);
			}

			if (!currentTouch->getIsMoved()) {
				if (!m_longTap) {
					if (m_singleTap) {
						m_GestureListener->onSingleTap(*currentTouch);
					}
				} else {
					m_GestureListener->onLongTap(*currentTouch);
				}
			}

			if (m_flingEnabled) {

				velocityTracker.trackEnd(touch);

				m_GestureListener->onFling(*currentTouch,
						velocityTracker.getVelocity(*currentTouch));

				velocityTracker.trackDestroy(touch);
			}

			if (m_target->getIsZoomable() || m_target->getIsRotatable()|| m_target->getIsMovable()) {
				if (m_firstTouch && m_firstTouch->getViewId()
						== touch.getViewId()) {
					CC_SAFE_DELETE(m_firstTouch);
				}

				if (m_secondTouch && m_secondTouch->getViewId()
						== touch.getViewId()) {
					CC_SAFE_DELETE(m_secondTouch);
				}
			}

			touch.setBubbled(currentTouch->getBubbled());
			touch.setHandled(currentTouch->getHandled());
		}

		m_touchHolder->removeTouch(touch);
	}

}

void KKGestureDirector::onGensture(KKTouch& touch) {
	bool validTouch = false;

	if (m_firstTouch && m_firstTouch->getViewId() == touch.getViewId()) {
		m_firstTouch->copy(touch);
		validTouch = true;
	} else if (m_secondTouch && m_secondTouch->getViewId() == touch.getViewId()) {
		m_secondTouch->copy(touch);
		validTouch = true;
	}

	if (validTouch) {

		CCNode* target = m_target->convertToCCNode();

		if (m_firstTouch && m_secondTouch) {

			CCPoint firstPoint = m_firstTouch->getRawPoint();
			CCPoint prevFirstPoint = m_firstTouch->getPrevRawPoint();
			CCPoint secondPoint = m_secondTouch->getRawPoint();
			CCPoint prevSecondPoint = m_secondTouch->getPrevRawPoint();

			CCPoint intersectPoint;

			/*if(ccpSegmentIntersect(firstPoint,secondPoint,prevFirstPoint,prevSecondPoint)){
				intersectPoint =  ccpIntersectPoint(firstPoint,secondPoint,prevFirstPoint,prevSecondPoint);
				intersectPoint = target->convertToNodeSpace(intersectPoint);

				CCLog("intersect \n");
			}else */
			{
				intersectPoint = ccpMidpoint(target->convertToNodeSpace(firstPoint),target->convertToNodeSpace(secondPoint));
			}

			CCPoint midPoint = ccp(intersectPoint.x/target->getContentSize().width,intersectPoint.y/target->getContentSize().height);

			m_target->setAnchorPointNotChangePosition(midPoint);

			if (m_target->getIsZoomable()) {

				CCPoint dis = ccpSub(secondPoint, firstPoint);
				CCPoint prevDis = ccpSub(prevFirstPoint, prevSecondPoint);

				float prevLen = ccpLength(prevDis);
				float len = ccpLength(dis);

				float scale = prevLen != 0 ? len / prevLen : 1;

				if (abs(prevLen - len) >= MIN_SCALE_DELTA) {
					m_GestureListener->onZoom(ccp(scale, scale));
				}

				CCLog("onzoom %f\n", scale);
			}

			if (m_target->getIsRotatable()) {

				const CCPoint unitPoint(1,0);

				float angle = ccpAngleSigned(ccpSub(secondPoint,firstPoint),unitPoint);
				float prevAngle = ccpAngleSigned(ccpSub(prevSecondPoint,prevFirstPoint),unitPoint);

				float degree = CC_RADIANS_TO_DEGREES(angle-prevAngle);

				if (abs(degree) >= MIN_ROTATE_DELTA) {
					m_GestureListener->onRotate(degree);
				}

				CCLog("onrotate %f,angle=%f,prevAngle=%f\n", degree,CC_RADIANS_TO_DEGREES(angle),CC_RADIANS_TO_DEGREES(prevAngle));

			}

			if (m_target->getIsMovable()) {

				midPoint = ccpAdd(target->getParent()->convertToNodeSpace(firstPoint),target->getParent()->convertToNodeSpace(secondPoint));

				CCPoint prevMidPoint = ccpAdd(target->getParent()->convertToNodeSpace(prevFirstPoint),target->getParent()->convertToNodeSpace(prevSecondPoint));

				m_GestureListener->onDrag(ccpMult(ccpSub(midPoint,prevMidPoint),0.25));

				CCLog("ondrag point=%f,%f,prevPoint=%f,%f\n",midPoint.x,midPoint.y,prevMidPoint.x,prevMidPoint.y);
			}

		}else if(m_firstTouch){
			if (m_target->getIsMovable()) {

				CCPoint firstPoint = m_firstTouch->getRawPoint();
				CCPoint prevFirstPoint = m_firstTouch->getPrevRawPoint();

				CCPoint deltaPoint = ccpSub(target->getParent()->convertToNodeSpace(firstPoint),target->getParent()->convertToNodeSpace(prevFirstPoint));
				m_GestureListener->onDrag(deltaPoint);
			}
		}
	}
}

void KKGestureDirector::onCheckSingleTap(ccTime time) {
	CCDirector::sharedDirector()->getScheduler()->unscheduleSelector(
			schedule_selector(KKGestureDirector::onCheckSingleTap), this);
	m_singleTap = false;
}

void KKGestureDirector::onCheckLongTap(ccTime time) {
	CCDirector::sharedDirector()->getScheduler()->unscheduleAllForTarget(this);

	KKTouch* touch = m_touchHolder->getTouch();

	if (touch && !touch->getIsMoved()) {
		m_longTap = true;
		m_GestureListener->onLongDown(*touch);
	}
}
